package org.deltaset.meta;

import org.deltaset.meta.extension.CatalogProvider;
import org.deltaset.meta.extension.ExecuteListenerProvider;
import org.deltaset.meta.extension.LabelResolverProvider;
import org.deltaset.meta.extension.MetaActionExecutorProvider;
import org.deltaset.meta.extension.MetaHandlerProvider;
import org.deltaset.meta.extension.OptionBuilderProvider;
import org.deltaset.meta.extension.ProviderDecorator;
import org.deltaset.meta.extension.RepositoryProvider;
import org.deltaset.meta.extension.TitleResolverProvider;
import org.deltaset.meta.internal.DefaultCatalogProvider;
import org.deltaset.meta.internal.DefaultExecuteListenerProvider;
import org.deltaset.meta.internal.DefaultLabelResolverProvider;
import org.deltaset.meta.internal.DefaultMetaActionExecutorProvider;
import org.deltaset.meta.internal.DefaultMetaHandlerProvider;
import org.deltaset.meta.internal.DefaultTitleResolverProvider;
import org.deltaset.meta.internal.DefaultMetaHandlerFactory;

/**
 *
 * @author dmakariev
 */
public class MetaHandlerFactoryBuilder {

    private CatalogProvider catalogProvider;
    private MetaHandlerProvider metaHandlerProvider;
    private LabelResolverProvider labelResolverProvider;
    private MetaActionExecutorProvider metaActionExecutorProvider;
    private OptionBuilderProvider optionBuilderProvider;
    private RepositoryProvider repositoryProvider;
    private TitleResolverProvider titleResolverProvider;
    private ExecuteListenerProvider executeListenerProvider;

    private MetaHandlerFactoryBuilder() {
        catalogProvider = null;//new DefaultCatalogProvider();
        metaHandlerProvider = new DefaultMetaHandlerProvider();
        labelResolverProvider = new DefaultLabelResolverProvider();
        metaActionExecutorProvider = new DefaultMetaActionExecutorProvider();
        optionBuilderProvider = null;//new DefaultOptionBuilderProvider();
        repositoryProvider = null;//new DefaultRepositoryProvider();
        titleResolverProvider = new DefaultTitleResolverProvider();
        executeListenerProvider = new DefaultExecuteListenerProvider();
    }

    public static MetaHandlerFactoryBuilder newBuilder() {
        return new MetaHandlerFactoryBuilder();
    }

    public MetaHandlerFactory build() {

        mustBeConfigured(repositoryProvider, RepositoryProvider.class);
        mustBeConfigured(optionBuilderProvider, OptionBuilderProvider.class);
        mustBeConfigured(catalogProvider, CatalogProvider.class);

        return new DefaultMetaHandlerFactory(
                catalogProvider,
                metaHandlerProvider,
                labelResolverProvider,
                metaActionExecutorProvider,
                optionBuilderProvider,
                repositoryProvider,
                titleResolverProvider,
                executeListenerProvider);
    }

    public MetaHandlerFactoryBuilder provider(final CatalogProvider provider) {
        this.catalogProvider = decorateProvider(provider, this.catalogProvider);
        return this;
    }

    public MetaHandlerFactoryBuilder catalog(final String catalogPath, final String unsupportedSrc, final String metaType) {
        this.catalogProvider = new DefaultCatalogProvider(catalogPath, unsupportedSrc, metaType);
        return this;
    }

    public MetaHandlerFactoryBuilder provider(final MetaHandlerProvider provider) {
        this.metaHandlerProvider = decorateProvider(provider, this.metaHandlerProvider);
        return this;
    }

    public MetaHandlerFactoryBuilder provider(final LabelResolverProvider provider) {
        this.labelResolverProvider = decorateProvider(provider, this.labelResolverProvider);
        return this;
    }

    public MetaHandlerFactoryBuilder provider(final MetaActionExecutorProvider provider) {
        this.metaActionExecutorProvider = decorateProvider(provider, this.metaActionExecutorProvider);
        return this;
    }

    public MetaHandlerFactoryBuilder provider(final OptionBuilderProvider provider) {
        this.optionBuilderProvider = decorateProvider(provider, this.optionBuilderProvider);
        return this;
    }

    public MetaHandlerFactoryBuilder provider(final RepositoryProvider provider) {
        this.repositoryProvider = decorateProvider(provider, this.repositoryProvider);
        return this;
    }

    public MetaHandlerFactoryBuilder provider(final TitleResolverProvider provider) {
        this.titleResolverProvider = decorateProvider(provider, this.titleResolverProvider);
        return this;
    }

    public MetaHandlerFactoryBuilder provider(final ExecuteListenerProvider provider) {
        this.executeListenerProvider = decorateProvider(provider, this.executeListenerProvider);
        return this;
    }

    private static <T> T decorateProvider(final T provider, final T defaultProvider) {

        if (provider instanceof ProviderDecorator) {
            ((ProviderDecorator<T>) provider).setDefaultProvider(defaultProvider);
            return provider;
        }
        return provider;
    }

    private static <T> void mustBeConfigured(T provider, Class<T> interfaceClass) {
        if (null == provider) {
            final String message = MetaHandlerFactoryBuilder.class.getName() + " ERROR not configured " + interfaceClass.getName();
            throw new IllegalArgumentException(message);
        }
    }
}
